package Gradle基础;

自定义Task类型
Gradle本身只是一个架子，真正起作用的是Task和Plugin。要真正了解Task和Plugin的工作机制并熟练运用，学会自定义Task类型和Plugin是大有裨益的。

Gradle中的Task要么是由不同的Plugin引入的，要么是我们自己在build.gradle文件中直接创建的。在默认情况下，我们所创建的Task是DefaultTask类型，
该类型是一个非常通用的Task类型，而在有些时候，我们希望创建一些具有特定功能的Task，比如Copy和Jar等。还有时候，我们希望定义自己创建的Task类型，在本
文中，我们以定义一个简单的HelloWorldTask为例，讲解如何自定义一个Task类型，并且如何对其进行配置。

在Gradle中，我们有3种方法可以自定义Task类型。

（一）在build.gradle文件中直接定义

我们知道，Gradle其实就是groovy代码，所以在build.gradle文件中，我们便可以定义Task类。

class HelloWorldTask extends DefaultTask {
  @Internal  //@Optional废弃
  String message = 'I am davenkin'

  @TaskAction
  def hello(){
      println "hello world $message"
  }
}

task hello1(type:HelloWorldTask)

task hello2(type:HelloWorldTask){
	message ="I am a programmer"
}

在上例中，我们定义了一个名为HelloWorldTask的Task，它需要继承自DefaultTask，它的作用是向命令行输出一个字符串。@TaskAction表示该Task要
执行的动作，即在调用该Task时，hello()方法将被执行。另外，message被标记为Internal(@Optional废弃)，表示在配置该Task时，message是可选
的。在定义好HelloWorldTask后，我们创建了两个Task实例，第一个hello1使用了默认的message值，而第二个hello2在创建时重新设置了message的值。

在执行hello时，命令行输出如下：
f:\new>gradle hello1

hello world I am davenkin

在执行hello2时，命令行输出如下：

f:\new>gradle hello2

hello world I am a programmer

（二）在当前工程中定义Task类型

在（一）中，我们在build.gradle中直接定义了Task的类型，这样将Task的定义和使用混在一起。在需要定义的Task类型不多时，我们可以采用这种方法，但是在项目
中存在大量的自定义Task类型时，这就不见得是中好的做法了。一种改进方法是在另外的一个gradle文件中定义这些Task，然后再apply到build.gradle文件中。这
里，我们将使用另一种方法：在buildSrc目录下定义Task类型，Gradle在执行时，会自动地查找该目录下所定义的Task类型，并首先编译该目录下的groovy代码以
供build.gradle文件使用。

在当前工程的buildSrc/src/main/groovy/davenkin目录下创建HelloWorldTask.groovy文件，将（1）中对HelloWorldTask的定义转移到该文件中：

package davenkin
import org.gradle.api.*
import org.gradle.api.tasks.*

class HelloWorldTask extends DefaultTask {
  @Internal
  String message = 'I am davenkin'

  @TaskAction
  def hello(){
      println "hello world $message"
  }
}

这里，我们将HelloWorldTask定义在了davenkin包下，因此在build.gradle文件中引用该Task时，我们需要它的全名称：

task hello1(type:davenkin.HelloWorldTask)


task hello2(type:davenkin.HelloWorldTask){
  message ="I am a programmer"
}

以上的hello1和hello2与（1）中的hello1和hello2完成的功能相同。


（三）在单独的项目中定义Task类型

虽然（2）中的Task定义与build.gradle分离开了，但是它依然只能应用在当前工程中。如果我们希望所定义的Task能够用在另外的项目中，那么（2）中的方法
便不可行的，此时我们可以将Task的定义放在单独的工程中，然后在所有使用Task的工程中通过声明依赖的方式引入这些Task。

创建另外一个项目，将（2）中buildSrc目录下的内容考到新建项目中，由于该项目定义Task的文件是用groovy写的，因此我们需要在该项目
的build.gradle文件中引入groovy Plugin。另外，由于该项目的输出需要被其他项目所使用，因此我们还需要将其上传到repository中，在本例中，我们
将该项目生成的包含了Task定义的jar文件上传到了本地的文件系统中。最终的build.gradle文件如下：

apply plugin: 'groovy'
apply plugin: 'maven'
version = '1.0'
group = 'davenkin'
archivesBaseName = 'hellotask'

repositories.mavenCentral()

dependencies {
  compile gradleApi()
  groovy localGroovy()
}
uploadArchives {
  repositories.mavenDeployer {
      repository(url: 'file:../lib')
  }
}


执行“gradle uploadArchives”，所生成的jar文件将被上传到上级目录的lib(../lib)文件夹中。

在使用该HelloWorldTask时，客户端的build.gradle文件可以做以下配置：

buildscript {
  repositories {
      maven {
          url 'file:../lib'
      }

  }

  dependencies {
      classpath group: 'davenkin', name: 'hellotask', version: '1.0'
  }
}

task hello(type: davenkin.HelloWorldTask)



首先，我们需要告诉Gradle到何处去取得依赖，即配置repository。另外，我们需要声明对HelloWorldTask的依赖，该依赖用于当前build文件。
之后，对hello的创建与（2）中一样


《创建Task的多种方法》
Gradle的Project从本质上说只是含有多个Task的容器，一个Task与Ant的Target相似，表示一个逻辑上的执行单元。我们可以通过很多种方式定义Task，所有的Task都存
放在Project的TaskContainer中。

（1）调用Project的task()方法创建Task

　　在使用Gradle时，创建Task最常见的方式便是：

task hello1 << {
   println 'hello1'
}
 

　　这里的“<<”表示追加的意思，即向hello中加入执行过程。我们还可以使用doLast来达到同样的效果：

task hello2 {
   doLast {
      println 'hello2'}
}
 

　　另外，如果需要向Task的最前面加入执行过程，我们可以使用doFirst：

task hello3 {
   doFirst {
      println 'hello3'}
}
 

　　在上面的3个例子中，Gradle的DSL向我们展示了一种非常自然的风格来创建Task，而事实上这些都只是一种内部DSL，也即必须符合groovy的语法要求。上面的task关
键字实际上是一个方法调用，该方法属于Project。Project中存在多个重载的task()方法。和Ruby等动态语言一样，在调用groovy方法时，我们不用将参数放在括号里面。

　　以上我们自定义的3个Task都位于TaskContainer中，Project中的tasks属性即表示该TaskContainer。为此，我们可以新建一个Task来显示这些信息：

task showTasks {
   println tasks.class
   println tasks.size()
}
 

　　将以上4个Task放在同一个build.gradle中，再执行gradle showTasks，命令行输出如下：

...
class org.gradle.api.internal.tasks.DefaultTaskContainer_Decorated
4
...

　　上面的DefaultTaskContainer_Decorated表示tasks类型，而4表示该TaskContainer中包含有4个自定义的Task——包括showTasks本身。

（2）通过TaskContainer的create()方法创建Task
　　在上文中我们讲到，通过task()方法创建的Task都被存放在了TaskContainer中，而Project又维护了一个TaskContainer类型的属性tasks，那么我们完全可以
直接向TaskContainer里面添加Task。查查TaskContainer的API文档可以发现，TaskContainer向我们提供了大量重载的create()方法用于添加Task。

tasks.create(name: 'hello4') << {
   println 'hello4'
}

（3）声明Task之间的依赖关系
　　Task之间是可以存在依赖关系，比如TaskA依赖TaskB，那么在执行TaskA时，Gradle会先执行TaskB，再执行TaskA。我们可以在定义一个Task的同时声明它的依赖关系：

task hello5(dependsOn:hello4) << {
    println 'hello5'
}

　　当然，我们也可以在定义Task之后再声明依赖：

task hello6 << {
   println 'hello6'
}

hello6.dependsOn hello5

（4）配置Task
　一个Task除了执行操作之外，还可以包含多个Property，其中有Gradle为每个Task默认定义的Property，比如description，logger等。另外，每一个特定的Task类型
还可以含有特定的Property，比如Copy的from和to等。当然，我们还可以动态地向Task中加入额外的Property。在执行一个Task之前，我们通常都需要先设定Property的
值，Gradle提供了多种方法设置Task的Property值。
首先，我们可以在定义Task的时候对Property进行配置：

task hello7 << {
   description = "this is hello7" 
   println description
}
 
我们还可以通过闭包的方式来配置一个已有的Task：

task hello8 << {
	println description
}

hello8 {
	description = "this is hello8"
}
 
　需要注意的是，对hello8的description设置发生在创建该Task之后，在执行“gradle hello8”时，命令行依然可以打印出正确的“this is hello8”，这是因
为Gradle在执行Task时分为两个阶段，首先是配置阶段，然后才是实际执行阶段。所以在执行hello8之前，Gradle会扫描整个build.gradle文档，将hello8
的description设置为“this is hello8”，然后执行hello8，此时hello8的description已经包含了设置后的值。

　　我们还可以通过调用Task的configure()方法完成Property的设置：


task hello9 << {
   println description
}

hello9.configure {
   description = "this is hello9"
}

　　实际上，通过闭包的方式配置Task在内部也是通过调用Task的configure()方法完成的。
